from threading import Thread
import subprocess
from argparse import ArgumentParser
import shlex
import gi
gi.require_versions({ 'Gtk': '4.0', 'Adw': '1' })
from gi.repository import Gtk, GLib, Adw # type: ignore

def run_task(command, on_output, on_finish):
    try:
        with subprocess.Popen(
            command,
            shell=True,
            stdout=subprocess.PIPE,
            stderr=subprocess.STDOUT,
            errors='replace'
        ) as proc:
            assert proc.stdout
            while True:
                output = proc.stdout.readline(100)
                if output == '':
                    break
                on_output(output)
    except Exception as e:
        on_output(f'{e}\n')
    on_finish()

def start_task(command, on_output, on_finish):
    Thread(target=run_task, args=(command, on_output, on_finish), daemon=True).start()

if __name__ == '__main__':
    parser = ArgumentParser()
    parser.add_argument('command')
    args = parser.parse_args()

    title = 'Gui Wrapper - ' + shlex.split(args.command)[0]

    def on_activate(app):
        window = Adw.ApplicationWindow(application=app, title=title)
        window.set_default_size(800, 600)

        view = Adw.ToolbarView()
        window.set_content(view)

        title_bar = Adw.HeaderBar()
        view.add_top_bar(title_bar)

        main_box = Gtk.Box(
            margin_start=10,
            margin_end=10,
            margin_top=10,
            margin_bottom=10,
            orientation=Gtk.Orientation.VERTICAL,
            spacing=10
        )
        view.set_content(main_box)

        output_scroll = Gtk.ScrolledWindow(hexpand=True, vexpand=True)
        output_scroll_prev_upper = None
        def on_output_resize(adj, _):
            nonlocal output_scroll_prev_upper
            page_size, upper, value = adj.props.page_size, adj.props.upper, adj.props.value
            prev_upper = output_scroll_prev_upper
            if upper == prev_upper:
                return
            if prev_upper is None or prev_upper - value - page_size < 1.0:
                adj.props.value = upper - page_size
            output_scroll_prev_upper = upper
        output_scroll.props.vadjustment.connect('notify::upper', on_output_resize)
        main_box.append(output_scroll)

        output_text = Gtk.TextView(
            wrap_mode=Gtk.WrapMode.CHAR,
            monospace=True,
            editable=False,
            cursor_visible=False
        )
        output_scroll.set_child(output_text)

        close_button = Gtk.Button(halign=Gtk.Align.CENTER, label='Close', sensitive=False)
        close_button.connect('clicked', lambda _: window.close())
        main_box.append(close_button)

        window.present()

        def on_output(output):
            buffer = output_text.props.buffer
            buffer.insert(buffer.get_end_iter(), output)

        def on_finish():
            close_button.props.sensitive = True

        start_task(
            args.command,
            lambda output: GLib.idle_add(on_output, output),
            lambda: GLib.idle_add(on_finish)
        )

    app = Adw.Application(application_id='icu.sunny.GuiWrapper')
    app.connect('activate', on_activate)

    app.run()
